#include<iostream>
#include<string>
#include<cstring>
#include<cstdlib>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"Thread.hpp"
#include"Comm.hpp"

using namespace ThreadModule;
void Usage(std::string proc)
{
    std::cout<<"Usage:\n\t"<<proc<<"serverip serverport\n"
        <<std::endl;
}

//初始化客户端
int INitClient(std::string &serverip,uint16_t serverport,struct sockaddr_in *server)
{
    //1:创建socket
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        std::cerr<<"socket error"<<std::endl;
        return -1;
    }
    //构建目标主机的socket信息
    //函数用于将指定的值填充到一段内存块中。它常用于对较大的结构体或数组进行清零操作
    memset(server,0,sizeof(struct sockaddr_in));
    server->sin_family = AF_INET;
    server->sin_port = htons(serverport);//主机字节序列转换为网络字节序列
    server->sin_addr.s_addr = inet_addr(serverip.c_str());//存储IP地址
    return sockfd;
}
//接收消息
void recvmessage(int sockfd,std::string name)
{
    while (true)
    {
       struct sockaddr_in peer;
       socklen_t len = sizeof(peer);
       char buffer[1024];
       ssize_t n = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
       if(n>0)
       {
        buffer[n] = 0;
        //stderr是无缓冲的，这意味着它会立即输出内容，而不需要等待缓冲区满或遇到换行符
        fprintf(stderr,"[%s]%s\n",name.c_str(),buffer);
       }
    }
}
//发送消息
void sendmessage(int sockfd,struct sockaddr_in &server,std::string name )
{
    std::string message;
    while(true)
    {
        printf("%s | Enter#",name.c_str());
        //使用 c_str() 方法时，应该注意确保在使用返回的 C 风格字符串之前，
        //原始的 std::string 对象仍然存在并且未被修改。这是因为返回的指针直接指向 
        //std::string 内部的数据，如果原始字符串对象被销毁或更改，通过 c_str() 
        //获得的指针可能会指向无效或过时的数据。
        fflush(stdout);//用于刷新输出缓冲区。当调用fflush()时
        //如果stream参数是一个输出流，它会将缓冲区中的数据写入到stream指定的文件中
        //如果stream是NULL，则会刷新所有打开的输出流。
        std::getline(std::cin,message);
        sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));
    }
}

int main(int argc,char *argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);//它的主要作用是将字符串转换为整数。可以处理不同进制的数字字符串，
    //并将它们转换为十进制的整数。
    struct sockaddr_in serveraddr;
    int sockfd = INitClient(serverip,serverport,&serveraddr);
    if(sockfd==-1)
    {
        return 1;
    }
    func_t r = std::bind(&recvmessage,sockfd,std::placeholders::_1);
    func_t s = std::bind(&sendmessage,sockfd,serveraddr,std::placeholders::_1);

    Thread Recver(r,"recver");
    Thread Sender(s,"sender");

    Recver.Start();
    Sender.Start();

    Recver.Join();
    Sender.Join();
    return 0;
}

